﻿using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

using ApiGateway;
using ApiGateway.Attr;
using HttpApiGateway.ActionFun;
using RpcHelper;

using WebSocketGateway.Interface;
using WebSocketGateway.Model;

using WebSocketGateway.Route;

namespace WebSocketGateway.Helper
{
    internal delegate void ExecAction(object target, object[] args);
    internal delegate object ExecFunc(object target, object[] args);
    internal class RouteHelper
    {
        public static ExecAction GetExecAction(MethodInfo method)
        {
            ParameterExpression target = Expression.Parameter(PublicDataDic.ObjectType, "target");
            ParameterExpression arrays = Expression.Parameter(typeof(object[]), "args");
            ParameterInfo[] parameters = method.GetParameters();
            Expression[] list = new Expression[parameters.Length];
            for (int i = 0; i < parameters.Length; i++)
            {
                list[i] = Expression.Convert(Expression.ArrayIndex(arrays, Expression.Constant(i)), parameters[i].ParameterType);
            }
            if (method.IsStatic)
            {
                MethodCallExpression methodExp = Expression.Call(method, list);
                return Expression.Lambda<ExecAction>(methodExp, target, arrays).Compile();
            }
            else
            {
                Expression exception = Expression.Convert(target, method.DeclaringType);
                MethodCallExpression methodExp = Expression.Call(exception, method, list);
                return Expression.Lambda<ExecAction>(methodExp, target, arrays).Compile();
            }
        }
        public static ExecFunc GetExecFunc(MethodInfo method)
        {
            ParameterExpression target = Expression.Parameter(PublicDataDic.ObjectType, "target");
            ParameterExpression arrays = Expression.Parameter(typeof(object[]), "args");
            ParameterInfo[] parameters = method.GetParameters();
            Expression[] list = new Expression[parameters.Length];
            for (int i = 0; i < parameters.Length; i++)
            {
                list[i] = Expression.Convert(Expression.ArrayIndex(arrays, Expression.Constant(i)), parameters[i].ParameterType);
            }
            if (method.IsStatic)
            {
                MethodCallExpression methodExp = Expression.Call(method, list);
                return Expression.Lambda<ExecFunc>(Expression.Convert(methodExp, PublicDataDic.ObjectType), target, arrays).Compile();
            }
            else
            {
                Expression exception = Expression.Convert(target, method.DeclaringType);
                MethodCallExpression methodExp = Expression.Call(exception, method, list);
                return Expression.Lambda<ExecFunc>(Expression.Convert(methodExp, PublicDataDic.ObjectType), target, arrays).Compile();
            }
        }
        public static ISocketApi GetApi(ApiModel model)
        {
            MethodInfo method = model.Method;
            if (method.ReturnType == PublicDataDic.VoidType)
            {
                return new VoidFuncApi(model);
            }
            return new ReturnFuncApi(model);
        }
        public static bool CheckFormat(MethodInfo method)
        {
            ParameterInfo[] param = method.GetParameters();
            if (param.Length == 0)
            {
                return true;
            }
            return !param.IsExists(a => a.IsOut || a.ParameterType.IsByRef);
        }

        public static ApiModel GetApiParam(MethodInfo method, IRouteConfig config, IApiModular modular)
        {
            ApiModel model = new ApiModel
            {
                Method = method,
                Type = config.Type,
                Prower = config.Prower,
                IsAccredit = config.IsAccredit,
                LocalPath = ApiGateway.Helper.ApiHelper.GetApiPath(config, modular, method, out bool isRegex)
            };
            if (isRegex)
            {
                throw new ErrorException("socket.gateway.not.use.regex");
            }
            ApiPrower attr = (ApiPrower)method.GetCustomAttribute(ApiPublicDict.ProwerAttr);
            if (attr != null)
            {
                model.IsAccredit = attr.IsAccredit;
                model.Prower = attr.Prower;
            }
            return model;
        }
    }
}
